package crp.services.api.graphql.loaders;

import com.qwlabs.lang.C2;
import com.qwlabs.lang.S2;
import crp.core.faculty.app.FacultyService;
import crp.core.faculty.domain.models.FacultyEntity;
import crp.core.security.app.AuthService;
import crp.core.security.app.RoleService;
import crp.core.security.domain.models.UserPermissions;
import crp.core.security.domain.models.UserRoleEntity;
import crp.core.student.app.StudentService;
import crp.core.student.domain.models.StudentEntity;
import crp.services.api.graphql.models.FacultyPayload;
import crp.services.api.graphql.models.RolePayload;
import crp.services.api.graphql.models.StudentPayload;
import crp.services.api.graphql.models.UserPayload;
import io.smallrye.graphql.api.Nullable;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Source;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
import java.util.Set;

@GraphQLApi
@RequestScoped
public class UserPayloadLoader {
    private final AuthService authService;
    private final FacultyService facultyService;
    private final StudentService studentService;
    private final RoleService roleService;

    @Inject
    public UserPayloadLoader(AuthService authService,
                             FacultyService facultyService,
                             StudentService studentService,
                             RoleService roleService) {
        this.authService = authService;
        this.facultyService = facultyService;
        this.studentService = studentService;
        this.roleService = roleService;
    }

    public List<@NotNull List<@NotNull RolePayload>> roles(@Source List<UserPayload> users) {
        Set<String> userIds = C2.set(users, UserPayload::getId, id -> true);
        Map<String, List<RolePayload>> userRoles = C2.listMap(roleService.searcher().findByUserIds(userIds),
                UserRoleEntity::getUserId, userRole -> RolePayload.of(userRole.getRole()));
        return C2.list(users, user -> userRoles.getOrDefault(user.getId(), List.of()));
    }

    @NotNull
    public List<@Nullable FacultyPayload> faculty(@Source List<UserPayload> users) {
        Set<String> facultyIds = C2.set(users, UserPayload::getFacultyId, S2::isNotEmpty);
        Map<String, FacultyPayload> faculties = C2.map(facultyService.searcher().findByIds(facultyIds),
                FacultyEntity::getId, FacultyPayload::of);
        return C2.list(users, user -> faculties.getOrDefault(user.getFacultyId(), null));
    }

    @NotNull
    public List<@Nullable StudentPayload> student(@Source List<UserPayload> users) {
        Set<String> facultyIds = C2.set(users, UserPayload::getStudentId, S2::isNotEmpty);
        Map<String, StudentPayload> students = C2.map(studentService.searcher().findByIds(facultyIds),
                StudentEntity::getId, StudentPayload::of);
        return C2.list(users, user -> students.getOrDefault(user.getStudentId(), null));
    }

    public List<@NotNull Set<@NotNull String>> permissions(@Source List<UserPayload> users) {
        Set<String> userIds = C2.set(users, UserPayload::getId, id -> true);
        Map<String, Set<String>> mapping = C2.map(
                authService.permissionLoader().load(userIds),
                UserPermissions::getUserId,
                UserPermissions::getPermissions);
        return C2.list(users, user -> mapping.getOrDefault(user.getId(), Set.of()));
    }
}
